#pragma once

#include <engine.core/AbstractDomStream.hpp>




namespace Engine
{
	namespace Edit
	{
		class Project;

		class ProjectItem
		{
		public:

			enum Type
			{
				FolderType, //< Default

				ScriptType,
				UiType,
				ProjectType,
			};

			enum Action
			{
				NameChanged,

				CanAddChild,
				BeginAddChild,
				EndAddChild,

				CanRemoveChild,
				BeginRemoveChild,
				EndRemoveChild,
			};

			enum Result
			{
				Agree,
				Disagree,
			};

			/**
			 * This combiner tests if a slot disagrees with an action.
			 * If one does, then the signal returns disagree, otherwise
			 * it returns agree (even if there is no slot).
			 */
			struct combiner
			{
				typedef Result   result_type;

				template <typename InputIterator>
				Result operator()(InputIterator first, InputIterator last) const
				{
					Result result = Agree;
					while(first != last)
					{
						if(*first == Disagree)
							result = Disagree;
						++first;
					}
					return result;
				}
			};

			SHARED(ProjectItem);

			typedef std::vector<ProjectItem::ptr>   Items;
			typedef boost::signals2::signal<Result(weak_ptr, weak_ptr, Action), combiner>   notify;

		protected:

			ProjectItem(QString name);
			ProjectItem(weak_ptr parent, QString name);
			virtual void post_create(ptr _this);

		public:

			MAKE_SHARED1(ProjectItem, QString);
			MAKE_SHARED2(ProjectItem, boost::weak_ptr<ProjectItem>, QString);



			/**
			 * @returns the type of item
			 */
			virtual Type type() const;

			/**
			 * @returns the name of the file
			 */
			QString name() const;

			/**
			 * Sets the name of this file.
			 */
			virtual void setName(QString name);



			/**
			 * Adds the given item to this one.
			 */
			void add(ProjectItem::ptr item);

			/**
			 * Removes the given item from this one.
			 */
			void remove(ProjectItem::ptr Items);



			/**
			 * @returns the first unused (under this one's children) name that matches the given pattern
			 * @throws exception in case pattern doesn't contain at least %1
			 */
			QString freeName(QString patterm) const;



			/**
			 * @returns the children of this item
			 */
			const Items& children() const;

			/**
			 * @returns the number of children
			 */
			std::size_t numChildren() const;

			/**
			 * @returns the child with the given name
			 */
			ptr child(QString name) const;

			/**
			 * @returns the nth child
			 */
			ptr child(std::size_t num) const;

			/**
			 * @returns the index of the given child in this one's list
			 * @throws when this is no child
			 */
			std::size_t index(ptr child) const;

			/**
			 * Tests if THIS item is the parent of the given one.
			 * It does not test if it's the DIRECT parent, but anyone along the tree.
			 */
			bool isParentOf(ProjectItem::ptr item) const;

			/**
			 * Iterates over all children and hands each child to the given functor.
			 */
			template <typename fn>
			void for_each_item(const fn& functor)
			{
				std::for_each(m_children.begin(), m_children.end(), functor);
			}



			/**
			 * Loads the project from the given stream.
			 */
			virtual void load(AbstractDomInputStream& stream);

			/**
			 * Saves this item to the given stream.
			 */
			virtual void save(AbstractDomOutputStream& stream) const;



			/**
			 * @returns a shared pointer to this item
			 */
			ptr shared_from_this();

			/**
			 * @returns a weak pointer to this item
			 */
			weak_ptr weak_from_this();

			/**
			 * @returns a pointer to the parent
			 */
			ptr parent();

			/**
			 * @returns a pointer to the root item
			 */
			boost::shared_ptr<Project> root();

		private:

			weak_ptr   m_this;
			weak_ptr   m_parent;

			QString    m_name;

			Items      m_children;
		};
		///////////////////////////////////////////////////////////////////////////////////////////

		Q_DECLARE_METATYPE(ProjectItem::ptr);
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
